// GTK.Application.cs - GTK Main Event Loop class implementation
//
// Author: Mike Kestner <mkestner@speakeasy.net>
//
// Copyright (c) 2001 Mike Kestner
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the Lesser GNU General 
// Public License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.

namespace Gtk {

	using Gdk;
	using System;
	using System.Reflection;
	using System.Runtime.InteropServices;
	using System.Threading;

	public partial class Application {

		const int WS_EX_TOOLWINDOW = 0x00000080;
		const int WS_OVERLAPPEDWINDOW = 0x00CF0000;

		[DllImport("user32.dll", EntryPoint = "CreateWindowExW", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
		static extern IntPtr Win32CreateWindow(int dwExStyle, string lpClassName, string lpWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lParam);

		[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
		static extern bool Win32DestroyWindow(IntPtr window);

		static Application() {
			if (!GLib.Thread.Supported)
				GLib.Thread.Init();

			switch (Environment.OSVersion.Platform) {
				case PlatformID.Win32NT:
				case PlatformID.Win32S:
				case PlatformID.Win32Windows:
				case PlatformID.WinCE:
					// No idea why we need to create that window, but it enables visual styles on the Windows platform
					IntPtr window = Win32CreateWindow(WS_EX_TOOLWINDOW, "static", "gtk-sharp visual styles window", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
					Win32DestroyWindow(window);
					break;
				default:
					break;
			}
		}

		[DllImport(Global.GtkNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern void gtk_init(ref int argc, ref IntPtr argv);

		[DllImport(Global.GtkNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern bool gtk_init_check(ref int argc, ref IntPtr argv);

		static void SetPrgname() {
			var args = Environment.GetCommandLineArgs();
			if (args != null && args.Length > 0)
				GLib.Global.ProgramName = System.IO.Path.GetFileNameWithoutExtension(args[0]);
		}

		public static void Init() {
			SetPrgname();
			IntPtr argv = new IntPtr(0);
			int argc = 0;

			gtk_init(ref argc, ref argv);

			SynchronizationContext.SetSynchronizationContext(new GLib.GLibSynchronizationContext());
		}

		static bool do_init(string progname, ref string[] args, bool check) {
			SetPrgname();
			bool res = false;
			string[] progargs = new string[args.Length + 1];

			progargs[0] = progname;
			args.CopyTo(progargs, 1);

			GLib.Argv argv = new GLib.Argv(progargs);
			IntPtr buf = argv.Handle;
			int argc = progargs.Length;

			if (check)
				res = gtk_init_check(ref argc, ref buf);
			else
				gtk_init(ref argc, ref buf);

			if (buf != argv.Handle)
				throw new Exception("init returned new argv handle");

			// copy back the resulting argv, minus argv[0], which we're
			// not interested in.

			if (argc <= 1)
				args = new string[0];
			else {
				progargs = argv.GetArgs(argc);
				args = new string[argc - 1];
				Array.Copy(progargs, 1, args, 0, argc - 1);
			}

			return res;
		}

		public static void Init(string progname, ref string[] args) {
			do_init(progname, ref args, false);
		}

		public static bool InitCheck(string progname, ref string[] args) {
			return do_init(progname, ref args, true);
		}

		[DllImport(Global.GtkNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern void gtk_main();

		public static void Run() {
			gtk_main();
		}

		[DllImport(Global.GtkNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern bool gtk_events_pending();


		public static bool EventsPending() {
			return gtk_events_pending();
		}

		[DllImport(Global.GtkNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern void gtk_main_iteration();

		[DllImport(Global.GtkNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern bool gtk_main_iteration_do(bool blocking);

		public static void RunIteration() {
			gtk_main_iteration();
		}

		public static bool RunIteration(bool blocking) {
			return gtk_main_iteration_do(blocking);
		}

		[DllImport(Global.GtkNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern void gtk_main_quit();

		public static void Quit() {
			gtk_main_quit();
		}


		[DllImport(Global.GtkNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern IntPtr gtk_get_current_event();

		public static Gdk.Event CurrentEvent {
			get {
				return Gdk.Event.GetEvent(gtk_get_current_event());
			}
		}

		internal class InvokeCB {
			EventHandler d;
			object sender;
			EventArgs args;

			internal InvokeCB(EventHandler d) {
				this.d = d;
				args = EventArgs.Empty;
				sender = this;
			}

			internal InvokeCB(EventHandler d, object sender, EventArgs args) {
				this.d = d;
				this.args = args;
				this.sender = sender;
			}

			internal bool Invoke() {
				d(sender, args);
				return false;
			}
		}

		public static void Invoke(EventHandler d) {
			InvokeCB icb = new InvokeCB(d);

			GLib.Timeout.Add(0, new GLib.TimeoutHandler(icb.Invoke));
		}

		public static void Invoke(object sender, EventArgs args, EventHandler d) {
			InvokeCB icb = new InvokeCB(d, sender, args);

			GLib.Timeout.Add(0, new GLib.TimeoutHandler(icb.Invoke));
		}
	}
}